import axios from 'axios'
// import { Loading } from 'element-ui'
// import mapLimit from 'async/mapLimit'

const instance = axios.create({
  timeout: 600 * 1000
})

instance.interceptors.request.use(function (config) {
  return config
}, function (error) {
  return Promise.reject(error)
})

instance.interceptors.response.use(function (response) {
  return response
}, function (error) {
  return Promise.reject(error)
})

/* eslint-disable */
const CHUNK_STATUS_DELETE = '0' // 删除
const CHUNK_STATUS_NOMARL = '1' // 正常
const CHUNK_STATUS_UNUPLOAD = '2' // 待上传
const CHUNK_STATUS_UNMERGE = '3' // 待合并
const CHUNK_STATUS_SUCCEED = '4' // 上传成功
/* eslint-enable */

const uploadByNomarl = (
  url = window.$app.fileUploadUrl,
  data,
  option = {
    branchCode: window.$app.gateway === window.$app.publicGateway
      ? window.$app.user.branchCode
      : `PRIVATE/${window.$app.user.branchCode}`
  },
  headers = {
    'Content-Type': 'multipart/form-data'
  },
  eleUploadOptions
) => {
  let formData = new FormData()
  let extendData = Object.assign({}, option, data.extendData)

  try {
    // 删除当前extendData中的file 解决重复 file问题
    delete extendData['file']
  } catch (error) {
  }
  // console.log('extendData:', JSON.parse(JSON.stringify(extendData)))

  for (const [key, value] of Object.entries(extendData)) {
    formData.append(key, value)
  }
  formData.append('file', data)

  try {
    // 解决M2平板 X5未加载 FormData.has不兼容问题
    if (!formData.has('branchCode')) {
      formData.append('branchCode', window.$app.gateway === window.$app.publicGateway
        ? window.$app.user.branchCode
        : `PRIVATE/${window.$app.user.branchCode}`)
    }
    if (!formData.has('timePoint')) {
      formData.append('timePoint', '00:00:01')
    }
    if (!formData.has('sourceDel')) {
      formData.append('sourceDel', '0')
    }
    if (!formData.has('ttlTimes')) {
      formData.append('ttlTimes', '7')
    }
    if (!formData.has('priority')) {
      formData.append('priority ', '9')
    }
    if (extendData.webkitdirectory) {
      let tmpstr = eleUploadOptions && eleUploadOptions.file && eleUploadOptions.file.webkitRelativePath ? eleUploadOptions.file.webkitRelativePath : ''
      formData.append('path ', tmpstr.substring(0, tmpstr.lastIndexOf('/') + 1))
      formData.append('uuid ', extendData.uuid || '')
    }
  } catch (error) {
    if (!extendData.branchCode) {
      formData.append('branchCode', window.$app.gateway === window.$app.publicGateway
        ? window.$app.user.branchCode
        : `PRIVATE/${window.$app.user.branchCode}`)
    }
  }

  let axiosOption = {
    method: 'post',
    url: url,
    data: formData,
    headers: { 'Content-Type': 'multipart/form-data', ...headers },
    onUploadProgress: eleUploadOptions.onProgress,
    cancelToken: data.axiosSource.token
  }

  try {
    // 解决M2平板 X5未加载 Object.fromEntries不兼容问题
    window.$twFrontLog.send({
      content: `这里正常的fs2 upload接口: data ${JSON.stringify(Object.fromEntries(formData))}, extendData:${extendData}`,
      moduleName: 'uploadByBreakPoint',
      linesOfCode: 43,
      templeName: 'uploadByBreakPoint.js'
    })
  } catch (error) { }

  return axios(axiosOption).then(res => {
    removeCurrentUploadFileList(data.name)
    return res
  }).catch(e => {
    window.$ui.msg({ type: 'error', message: '文件上传失败,请重新上传' })
    // const err = new Error('文件上传失败')
    // err.status = 500
    // err.method = 'post'
    // err.url = url
    // try {
    //   eleUploadOptions.onError(err, data)
    // } catch (error) { }
    removeCurrentUploadFileList(data.name)
    return Promise.reject(e)
  })
}

// 分片上传
const uploadByPieces = async (url, { name, file, Md5Code }, option = {
  branchCode: window.$app.gateway === window.$app.publicGateway
    ? window.$app.user.branchCode
    : `PRIVATE/${window.$app.user.branchCode}`
}, eleUploadOptions, fileMd5Loading = false) => {
  const chunkSize = parseInt(window.$app.platConfig['paas_platform_file_upload_file_slice_size']) || 20 * 1024 * 1024 // 20MB一片 后续根据配置项来决定
  // const chunkSize = 5 * 1024 * 1024
  const chunkCount = Math.ceil(file.size / chunkSize) // 总片数
  const percentage = []
  const optionFile = eleUploadOptions.file
  const asyncJS = require('async')
  /* eslint-disable */
  let fileChunkedList = [] // 文件分片完成之后的数组
  let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
  let currentChunk = 0
  let fileMd5Progress = 0
  /* eslint-enable */
  const getChunkInfo = (file, index) => {
    let start = index * chunkSize
    let end = Math.min(file.size, start + chunkSize)
    let chunk = file.slice(start, end)
    return { start, end, chunk }
  }
  // 创建分片
  const createChunk = (data) => {
    return new Promise((resolve, reject) => {
      let createChunkData = data
      window.$twFrontLog.send({
        content: `创建分片信息: ${JSON.stringify(createChunkData)}`,
        moduleName: 'createChunk',
        linesOfCode: 0,
        templeName: 'uploadByBreakPoint.js'
      })
      // saveCurrentUploadFileList(data.fileName)
      if (createChunkData && createChunkData.webkitdirectory) {
        let tmpstr = createChunkData.file && createChunkData.file.webkitRelativePath ? createChunkData.file.webkitRelativePath : ''
        data.path = tmpstr.substring(0, tmpstr.lastIndexOf('/') + 1)
        data.uuid = createChunkData.uuid || ''
      }
      instance({
        url: url, // window.$app.gateway + '/fs/chunk/create',
        method: 'post',
        data,
        headers: {
          branchCode: window.$app.gateway === window.$app.publicGateway
            ? window.$app.user.branchCode
            : `PRIVATE/${window.$app.user.branchCode}`,
          'X-Client-Language': 'zh_CN',
          'Access-Token': window.$app.user.token,
          'X-User-Account': window.$app.user.userId
        }
      }).then(async res => {
        console.log('createChunk res:', res)
        window.$twFrontLog.send({
          content: `创建分片信息接口返回的数据: ${JSON.stringify(res)}`,
          moduleName: 'createChunk response data',
          linesOfCode: 0,
          templeName: 'uploadByBreakPoint.js'
        })
        const { data } = res
        // 兼容v2/upload统一后端返回值
        if (!data.hasOwnProperty('serverReults')) {
          data.serverReults = data.serverResult
        }
        if (!data.hasOwnProperty('fileInfos')) {
          data.fileInfos = data.responseEntity
        }

        if (data.serverResult && data.serverResult.resultCode !== '200') {
          window.$ui.msg({ type: 'error', message: '文件上传失败,请重新上传' })
          eleUploadOptions.onError(res)
          removeCurrentUploadFileList(createChunkData.fileName)
          return
        }

        if (data.responseEntity && !window.$ui.isEmptyValue(data.responseEntity)) {
          const fileInfo = data.responseEntity[0]
          if (fileInfo.exist) {
            // 文件存在直接返回
            console.log('文件存在直接返回：', fileInfo)
            removeCurrentUploadFileList(fileInfo.fileName)
            // eleUploadOptions.onSuccess(res.data)
            return resolve(res.data)
          } else if (!window.$ui.isEmptyValue(createChunkData.fileId)) {
            // 有fileId就是已经创建过分片任务, 已经分片好 此时data.responseEntity返回的是分片信息
            let unUploadChunks = []
            data.responseEntity.map(chunkItem => {
              unUploadChunks.push(chunkItem)
            })

            asyncJS.mapLimit(unUploadChunks, window.$app.platConfig['paas_platform_file_upload_file_slice_num'] || 2, ({ fileId, seq }, cb) => {
              let index = seq - 1
              console.log('chunk runing index:', index, 'seq:', seq, fileId)
              readChunkLimit(index, fileId, seq, cb)
            }, (err, result) => {
              // console.log('mapLimit err:', err, result, err.message)
              removeCurrentUploadFileList(fileInfo.fileName || createChunkData.fileName)
              if (err) {
                window.$ui.msg({ type: 'error', message: '文件上传失败,请重新上传' })
                reject(err)
                eleUploadOptions.onError(err)
                return
              }
              // 包装成跟后端返回一样的字段，兼容业务里面的uploadSuccess用到的字段
              let chunkResponseData = {
                fileInfos: [getLocalChunk(createChunkData.md5)],
                serverReults: {
                  internalMessage: null,
                  resultCode: '200',
                  resultMessage: '成功'
                }
              }
              // eleUploadOptions.onSuccess(chunkResponseData)
              return resolve(chunkResponseData)
            })
          } else {
            const promiseList = []
            for (let index = 0; index < chunkCount; ++index) {
              promiseList.push({ seq: index + 1, fileId: fileInfo.fileId, index })
            }

            console.log('chunkCount:', chunkCount)
            let saveChunkData = {
              fileName: fileInfo.fileName,
              fileId: fileInfo.fileId,
              fileSize: file.size,
              size: file.size,
              format: fileInfo.format,
              localPath: fileInfo.localPath,
              md5: fileInfo.md5
            }
            if (createChunkData && createChunkData.webkitdirectory) {
              let tmpstr = createChunkData.file && createChunkData.file.webkitRelativePath ? createChunkData.file.webkitRelativePath : ''
              saveChunkData.path = tmpstr.substring(0, tmpstr.lastIndexOf('/') + 1)
              saveChunkData.uuid = createChunkData.uuid || ''
            }
            saveChunk(saveChunkData)
            asyncJS.mapLimit(promiseList, window.$app.platConfig['paas_platform_file_upload_file_slice_num'] || 2, ({ seq, fileId, index }, cb) => {
              console.log('new chunk runing index:', index, fileId, 'seq:', seq)
              readChunkLimit(index, fileId, seq, cb)
            }, (err, result) => {
              removeCurrentUploadFileList(createChunkData.fileName)
              if (err) {
                window.$ui.msg({ type: 'error', message: '文件上传失败,请重新上传' })
                eleUploadOptions.onError(err)
                return reject(err)
              }
              // eleUploadOptions.onSuccess(res.data)
              return resolve(res.data)
            })
          }
        }
      }).catch(err => {
        eleUploadOptions.onError(err)
        window.$twFrontLog.send({
          content: `创建分片信息出错了: ${JSON.stringify(err)}`,
          moduleName: 'createChunk catch err',
          linesOfCode: 0,
          templeName: 'uploadByBreakPoint.js'
        })
        window.$ui.msg({ type: 'error', message: '文件上传失败,请重新上传' })
        removeCurrentUploadFileList(data.fileName)
        return reject(err)
      })
    })
  }
  // 文件MD5 用第一个分片进行MD5
  // eslint-disable-next-line
  const countMd5 = () => {
    return new Promise(function async (resolve, reject) {
      const SparkMD5 = require('spark-md5')
      const fileReader = new FileReader()
      fileReader.readAsBinaryString(file)
      // for (let i = 0; i < file.size; i = i + chunkSize) {
      //   const tmp = file.slice(i, Math.min((i + chunkSize), file.size))
      //   if (i === 0) {
      //     // fileReader.readAsBinaryString(tmp)
      //     fileReader.readAsArrayBuffer(tmp)
      //   }
      //   fileChunkedList.push(tmp)
      // }
      fileReader.onload = e => {
        return resolve(SparkMD5.hashBinary(e.target.result).toLocaleLowerCase())
      }
    })
  }

  const readChunkLimit = (index, fileId, seq, cb = e => e) => {
    const { chunk } = getChunkInfo(file, index)
    let blobToFile = new File([chunk], file.name, { type: file.type })
    console.log('readChunk chunk:', chunk, 'blobToFile:', blobToFile)
    let fetchForm = new FormData()
    fetchForm.append('file', blobToFile)
    fetchForm.append('fileId', fileId)
    fetchForm.append('seq', seq)

    window.$twFrontLog.send({
      content: `开始上传分片信息, 当前上传的fileId:${fileId}, 分片下标:${seq}, 分片:${JSON.stringify(fetchForm)}`,
      moduleName: 'readChunkLimit',
      linesOfCode: 0,
      templeName: 'uploadByBreakPoint.js'
    })
    return instance({
      url: window.$app.gateway + '/fs/chunk/upload',
      method: 'post',
      cancelToken: file.axiosSource.token,
      data: fetchForm,
      headers: {
        'Content-Type': 'multipart/form-data',
        branchCode: window.$app.gateway === window.$app.publicGateway
          ? window.$app.user.branchCode
          : `PRIVATE/${window.$app.user.branchCode}`,
        'X-Client-Language': 'zh_CN',
        'Access-Token': window.$app.user.token,
        'X-User-Account': window.$app.user.userId
      },
      onUploadProgress (e) {
        if (e.total > 0) {
          e.percent = e.loaded / e.total * 100
        }
        percentage[index] = e.loaded
        updataPercentage(e)
      }
    }).then(res => {
      cb(null, {
        data: res.data
      })
    }).catch(err => {
      cb(err)
    })
  }

  // 更新上传进度条百分比的方法
  const updataPercentage = (e) => {
    let loaded = 0// 当前已经上传文件的总大小
    percentage.forEach(item => {
      loaded += item
    })
    e.percent = loaded / optionFile.size * 100
    eleUploadOptions.onProgress(e)
  }

  // 保存当前文件信息以及切片信息
  const saveChunk = (data) => {
    const saveKey = 'localStorage_file_breakpoint'
    const localstorageFile = localStorage.getItem(saveKey)
    const { userId } = window.$app.user
    const saveFileObj = Object.assign({ name: file.name, chunkCount, chunkSize }, data)
    console.log('saveFileObj:', saveFileObj)
    try {
      // 获取本地现有数据
      const existData = JSON.parse(localstorageFile)
      if (existData && existData[userId] && existData[userId][data.md5]) {
        existData[userId][data.md5] = Object.assign(saveFileObj, existData[userId][data.md5])
        localStorage.setItem(saveKey, JSON.stringify(existData))
      } else {
        existData[userId][data.md5] = saveFileObj
        localStorage.setItem(saveKey, JSON.stringify(existData))
      }
    } catch (error) {
      localStorage.setItem(saveKey, JSON.stringify({ [userId]: { [data.md5]: saveFileObj } }))
    }
  }

  // 获取当前缓存中的文件
  const getLocalChunk = (md5) => {
    const saveKey = 'localStorage_file_breakpoint'
    const localstorageFile = localStorage.getItem(saveKey)
    const { userId } = window.$app.user
    try {
      const existData = JSON.parse(localstorageFile)
      return existData[userId][md5]
    } catch (error) {
      return null
    }
  }

  return new Promise(async (resolve, reject) => {
    const loadingConfig = window.$app.appType === 'mobile' ? {
      message: '文件解析中...',
      forbidClick: true,
      loadingType: 'spinner'
    } : {
      text: '文件解析中...',
      spinner: 'el-icon-loading',
      background: 'rgba(0, 0, 0, 0.5)',
      fullscreen: false,
      lock: false
    }
    let loadingInstance = fileMd5Loading ? window.$ui.loading(loadingConfig) : null
    try {
      window.$twFrontLog.send({
        content: `开始计算文件MD5值`,
        moduleName: 'uploadByBreakPoint',
        linesOfCode: 249,
        templeName: 'uploadByBreakPoint.js'
      })
      let createChunkData = {
        branchCode: window.$app.gateway === window.$app.publicGateway
          ? window.$app.user.branchCode
          : `PRIVATE/${window.$app.user.branchCode}`,
        fileId: '',
        fileName: file.name,
        fileSize: file.size,
        size: file.size,
        // md5: Md5Code,
        total: chunkCount,
        timePoint: '00:00:01',
        priority: '9',
        sourceDel: '0',
        ttlTimes: '7',
        chunkSize,
        ...option
      }

      const fileMd5 = new FileMD5()
      fileMd5.md5(file,
        async (md5Result) => {
          createChunkData.md5 = md5Result
          window.$twFrontLog.send({
            content: `web选择文件MD5计算完后的值:${JSON.stringify(createChunkData)}`,
            moduleName: 'uploadByBreakPoint',
            linesOfCode: 265,
            templeName: 'uploadByBreakPoint.js'
          })
          const localChunk = getLocalChunk(md5Result)
          if (localChunk) {
            createChunkData.fileId = localChunk.fileId
            window.$twFrontLog.send({
              content: `获取当前本地的Chunk:${JSON.stringify(localChunk)}`,
              moduleName: 'uploadByBreakPoint',
              linesOfCode: 274,
              templeName: 'uploadByBreakPoint.js'
            })
          }
          // console.log('createChunkData:', createChunkData, localChunk)
          const resq = await createChunk(createChunkData)
          resolve(resq)
        },
        progress => {
          if (fileMd5Loading) {
            loadingInstance.text = `文件解析中, 当前进度：` + (progress * 100).toFixed(2) + '%'
            loadingInstance.message = loadingInstance.text
          }

          if (progress === 1) {
            if (window.$app.appType === 'mobile') {
              loadingInstance && loadingInstance.clear()
            } else {
              loadingInstance && loadingInstance.close()
            }
          }
        })
    } catch (error) {
      if (window.$app.appType === 'mobile') {
        loadingInstance && loadingInstance.clear()
      } else {
        loadingInstance && loadingInstance.close()
      }
      console.log('error:', error)
      reject(error)
    }
  })
}

export function getCurrentUploadFileList () {
  const saveKey = 'localStorage_file_breakpoint_uploading'
  const uploadingFileList = localStorage.getItem(saveKey)
  return Array.isArray(uploadingFileList) ? uploadingFileList : JSON.parse(localStorage.getItem(saveKey)) || []
}

// 当前正在上传的分片列表, 用于判断如果同时上传的文件>2则不进行分片上传, 用普通的上传方式
export function saveCurrentUploadFileList (fileName) {
  const saveKey = 'localStorage_file_breakpoint_uploading'
  let uploadingFiles = getCurrentUploadFileList()
  uploadingFiles.push(fileName)

  localStorage.setItem(saveKey, JSON.stringify(uploadingFiles))
}

export function removeCurrentUploadFileList (fileName) {
  const saveKey = 'localStorage_file_breakpoint_uploading'
  let uploadingFiles = getCurrentUploadFileList()
  let uploadingFilesIndex = uploadingFiles.findIndex(item => item === fileName)
  if (uploadingFilesIndex > -1) {
    uploadingFiles.splice(uploadingFilesIndex, 1)
  }
  console.log('remove fileName:', fileName)
  localStorage.setItem(saveKey, JSON.stringify(uploadingFiles))
  window.$twFrontLog.send({
    content: `remove fileName: ${fileName} in localStorage_file_breakpoint_uploading`,
    moduleName: 'uploadByBreakPoint',
    linesOfCode: 274,
    templeName: 'uploadByBreakPoint.js'
  })
}

class FileMD5 {
  md5 (file, md5Fn, progressFn) {
    this.aborted = false
    this.progress = 0
    let currentChunk = 0
    const blobSlice =
      File.prototype.slice ||
      File.prototype.mozSlice ||
      File.prototype.webkitSlice
    const chunkSize = 2097152
    const chunks = Math.ceil(file.size / chunkSize)
    const SparkMD5 = require('spark-md5')
    const spark = new SparkMD5.ArrayBuffer()
    const reader = new FileReader()

    loadNext()

    reader.onloadend = e => {
      spark.append(e.target.result) // Append array buffer
      currentChunk++
      this.progress = currentChunk / chunks

      if (progressFn && typeof progressFn === 'function') {
        progressFn(this.progress)
      }

      if (this.aborted) {
        md5Fn('aborted')
        return
      }

      if (currentChunk < chunks) {
        loadNext()
      } else {
        md5Fn(spark.end())
      }
    }

    function loadNext () {
      const start = currentChunk * chunkSize
      const end = start + chunkSize >= file.size ? file.size : start + chunkSize
      reader.readAsArrayBuffer(blobSlice.call(file, start, end))
    }
  }

  abort () {
    this.aborted = true
  }
}

export { uploadByNomarl, uploadByPieces }
